package com.hitqz.robot.biz.websocket.endpoint;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hitqz.robot.api.business.dto.*;
import com.hitqz.robot.api.business.entity.PatrolResultEntity;
import com.hitqz.robot.biz.config.CustomConstant;
import com.hitqz.robot.biz.service.PatrolResultService;
import com.hitqz.robot.common.security.service.RobotUser;
import com.hitqz.robot.biz.websocket.config.CustomConfigurator;
import jakarta.annotation.PreDestroy;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@ServerEndpoint(value = "/websocket/subscribe/{topic}",configurator = CustomConfigurator.class)
@Component
@Slf4j
public class SubscriptionEndpoint {
	// ObjectMapper for JSON parsing
	private static final ObjectMapper objectMapper = new ObjectMapper();
	// 存储话题和session的映射关系
	public static final Map<String, CopyOnWriteArraySet<Session>> topicSubscribers = new ConcurrentHashMap<>();
	// 用于存储所有打开的WebSocket sessions
	private static final CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>();


	// 全局维护每个 Session 的待发送消息队列和发送状态
	private static final ConcurrentMap<Session, AtomicBoolean> sessionSendingFlags = new java.util.concurrent.ConcurrentHashMap<>();


    private static boolean customPanelEnable;

    private static String customName;

    @Value("${robot.custom-panel.enable:false}")
    private void setCustomPanel(Boolean enable ){
        customPanelEnable = enable;
    }

    @Value("${robot.custom-panel.name:default}")
    private void setCustomPanel(String name){
        customName = name;
    }



	@OnOpen
	public void onOpen(Session session,@PathParam(value = "topic") String topic) {
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		sessions.add(session);
		log.debug("[ws][subscribe]{},session-id:{} connected.current size :{}", user.getName(),session.getId(),sessions.size());
		//订阅话题
		subscribe(session, topic);
	}

	// 订阅话题   客户端需要发送一个JSON消息，格式为：{"action":"subscribe","topic":"topicName"}
	@OnMessage
	public void onMessage(Session session,String message) {
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		log.info("[ws][subscribe]{},message:{},session-id:{}", user.getName(),message,session.getId());
	}

	@OnClose
	public void onClose(Session session,@PathParam(value = "topic") String topic) {
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		//取消订阅
		unsubscribe(session, topic);
		// 移除session
		sessions.remove(session);
		log.debug("[ws][subscribe]{},session-id:{} disconnected.current size :{}", user.getName(),session.getId(),sessions.size());
	}

	@OnError
	public void onError(Session session,Throwable error) {
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		log.error("[ws][subscribe]{},session-id:{},error:{}", user.getName(),session.getId(),error.getMessage());
	}

	// 话题广播
	public static void broadcast(String topic, String message) {
		// 等待所有 session 发送任务完成
		try {
			//检查是否有订阅者
			if (!topicSubscribers.containsKey(topic)) {
				return;
			}

			// =================== 性能优化：将数据库查询移出循环 ===================
			String enrichedMessage = message;
			if (Objects.equals(topic, "summary")) {
				List<RobotClientSummaryDto> dtoList = JSONArray.parseArray(message, RobotClientSummaryDto.class);
                // 按clientId降
                dtoList.sort(Comparator.comparing(RobotClientSummaryDto::getClientId).reversed());
				// 预处理：遍历一次列表，为所有dto附加告警信息
				for (RobotClientSummaryDto dto : dtoList) {
					taskWarning(dto);
                    //处理按钮
                    dealButton(dto);
				}
				enrichedMessage = JSONArray.toJSONString(dtoList);
			}
			// =================================================================

			CopyOnWriteArraySet<Session> subscribers = topicSubscribers.get(topic);
			if (subscribers.isEmpty()) {
				topicSubscribers.remove(topic);
				return;
			}
			for (Session session : subscribers) {
				// 获取或创建 AtomicBoolean（线程安全）
				AtomicBoolean sendingFlag = sessionSendingFlags.computeIfAbsent(
						session,
						k -> new AtomicBoolean(false)
				);
				if (sendingFlag.compareAndSet(false, true)) {
					if (!session.isOpen()) {
						sessionSendingFlags.remove(session);
						continue;
					}
					// 筛选信息 新建变量而不是修改message的值
					String finalMessage = enrichedMessage;
//					websocketSendThreadPool.execute(() -> {
						try {
							if (StrUtil.isNotBlank(finalMessage)) {
								session.getBasicRemote().sendText(finalMessage);
							}
						} catch (Exception e) {
							log.error("[ws][subscribe]send message error:{}", e.getMessage());
						} finally {
							sessionSendingFlags.get(session).set(false);
						}
//					});
				}
			}
		} catch (Exception e) {
			log.error("[ws][subscribe]message sending was error:{}", e.getMessage());
		}
	}

    // 订阅话题
	public static void subscribe(Session session, String topic) {
		topicSubscribers.computeIfAbsent(topic, k -> new CopyOnWriteArraySet<>()).add(session);
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		log.info("[ws][subscribe]{},session-id:{} subscribe topic:{}",user.getName(),session.getId(),topic);
	}

	// 取消订阅
	public static void unsubscribe(Session session, String topic) {
		// 移除订阅session
		if (topicSubscribers.containsKey(topic)) {
			topicSubscribers.get(topic).remove(session);
			// 进一步移除话题
			if (topicSubscribers.get(topic).isEmpty()) {
				topicSubscribers.remove(topic);
			}
			RobotUser user =(RobotUser) session.getUserProperties().get("user");
			log.info("[ws][subscribe]{},session-id:{} unsubscribe topic:{}",user.getName(),session.getId(),topic);
		}
	}

	private void sendError(Session session, String errorMessage) {
		RobotUser user =(RobotUser) session.getUserProperties().get("user");
		try {
			session.getBasicRemote().sendText("{\"error\":\"" + errorMessage + "\"}");
		} catch (IOException e) {
			// 处理发送错误消息时的异常
			log.error("[ws][subscribe]send to {} error message failed:{}",user.getName(),e.getMessage());
		}finally {
			try {
				// 在发送错误信息后关闭会话
				session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, errorMessage));
			} catch (IOException e) {
				// 处理关闭session时的异常
				log.error("[ws][subscribe]close {}, session-id:{} failed:{}",user.getName(),session.getId(),e.getMessage());
			}
		}
	}



    private static void dealButton(RobotClientSummaryDto dto) {
        List<RobotControlButtonDto> buttons = dto.getControlPanel().getButtons();
        // 过滤掉不安全的按钮
//        buttons = buttons.stream().filter(f->!Objects.equals(f.getAction(),"relocation")).toList();
        dto.getControlPanel().setButtons(buttons);
        if (customPanelEnable && Objects.equals(customName,"bjsys")) {
            List<RobotEquipmentPaneButtonsDto> equipmentPanels = dto.getEquipmentPanel().getPanels();
            RobotEquipmentPaneButtonsDto customPanel = new RobotEquipmentPaneButtonsDto();
            customPanel.setTitle("皮带任务面板");
            ArrayList<RobotEquipmentControlButtonDto> list = new ArrayList<>();
            if (CustomConstant.modeEnable.get()) {
                list.add(RobotEquipmentControlButtonDto.createButton("联动已开启","confirm","custom-mode","{\"action\": \"custom-mode\"}","kz_btn_45_p","kz_btn_45_p1","kz_btn_45_s",false));
            }else {
                list.add(RobotEquipmentControlButtonDto.createButton("联动禁用中","confirm","custom-mode","{\"action\": \"custom-mode\"}","kz_btn_45","kz_btn_45_p","kz_btn_45_s",false));
            }
            if (CustomConstant.plcStatus.get()){
                list.add(RobotEquipmentControlButtonDto.createButton("皮带已开启","confirm","custom-plc","{\"action\": \"custom-plc\"}","kz_btn_46_p","kz_btn_46_p1","kz_btn_46_s",false));
            }else{
                list.add(RobotEquipmentControlButtonDto.createButton("皮带待机中","confirm","custom-plc","{\"action\": \"custom-plc\"}","kz_btn_46","kz_btn_46_p","kz_btn_46_s",false));
            }
            customPanel.setButtons(list);
            //插入到第一个
            equipmentPanels.add(0,customPanel);
        }
    }

	private static void taskWarning(RobotClientSummaryDto dto ){
		RobotTaskDto task = dto.getTask();
		if (task==null){
			return;
		}
        List<Integer> lastTenServerIds = task.getLastTenServerIds();
        if (lastTenServerIds.isEmpty()){
			return;
		}
		PatrolResultService patrolResultService = SpringUtil.getBean("patrolResultService");
		List<PatrolResultEntity> list = patrolResultService.list(Wrappers.<PatrolResultEntity>lambdaQuery()
				.eq(PatrolResultEntity::getStatus, 2) // 检测异常项
                .in(PatrolResultEntity::getAuditStatus, 0,1) //未审核或者正报
                .eq(PatrolResultEntity::getUploadStatus,0)
				.in(PatrolResultEntity::getTicketsId, lastTenServerIds) //最近的10个报警
				.last("limit 10"));
		if (list.isEmpty()){
			return;
		}
		List<PatrolResultWarningDto> patrolResultWarningDtos = BeanUtil.copyToList(list, PatrolResultWarningDto.class, CopyOptions.create().setFieldValueEditor((fieldName, fieldValue) -> {
			if (fieldName.contains("Time") && StrUtil.isNotBlank((String)fieldValue)) {
				return DateUtil.format(DateUtil.parse((String)fieldValue), "yyyy-MM-dd HH:mm:ss");
			}
			return fieldValue;
		}));
		dto.setErrorChecks(patrolResultWarningDtos);
	}


    @PreDestroy
    public void onDestroy() {
        log.info("Closing all WebSocket connections");
        for (Session session : sessions) {
            try {
                if (session.isOpen()) {
                    session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Application is shutting down"));
                }
            } catch (IOException e) {
                log.error("Error closing WebSocket session: " + session.getId(), e);
            }
        }
    }


}
